home *** CD-ROM | disk | FTP | other *** search
/ The World's Largest Collection of Windows Software / The World's Largest Collection of Windows Software - Disc 1.iso / connect / _j2 / wvnsc926 / wvgroup.c < prev    next >
C/C++ Source or Header  |  1994-09-21  |  54KB  |  1,820 lines

  1. /********************************************************************
  2.  *                                                                  *
  3.  *  MODULE    :  WVGROUP.C                                          *
  4.  *                                                                  *
  5.  *  PURPOSE   : This file contains the window procedure for the     *
  6.  *              Group viewing window for WinVN.                     *
  7.  *                                                                  *
  8.  *  FUNCTIONS :                                                     *
  9.  *              WinVnViewWndProc()    -  Window Procedure for any   *
  10.  *                                       of WinVN's group windows   *
  11.  *                                                                  *
  12.  *              FileLength()          -  Find the size in bytes of  *
  13.  *                                       a file                     *
  14.  *                                                                  *
  15.  *              ViewArticle()         -  Requests and views an      *
  16.  *                                       article from the server    * 
  17.  *                                                                  *
  18.  *              setArticleFocus()     -  Makes an article active    *
  19.  *                                                                  *
  20.  *              UnlinkArtsInGroup()   -  Prepares article window    *
  21.  *                                       for deletion               *
  22.  *                                                                  *
  23.  *              UpdateSeenArts()      -  Save seen info in document *
  24.  *                                                                  *
  25.  *              CursorToTextLine()    -  Locates text in a document *
  26.  *                                       based on cursor position   *
  27.  *                                                                  *
  28.  *              search_headers()      -  Search all headers for a   *
  29.  *                                       given substring            *
  30.  *                                                                  *
  31.  *    string_compare_insensitive()    -  Compare two strings while  *
  32.  *                                       ignoring case sensitivity  *
  33.  *                                                                  *
  34.  *               AffectSelected()     -  Set selected flag based on *
  35.  *                                       search criteria            *
  36.  *                                                                  *
  37.  ********************************************************************/
  38.  
  39.  
  40. /*
  41.  * $Id: wvgroup.c 1.49 1994/09/18 22:49:02 jcooper Exp $
  42.  *
  43.  */
  44.  
  45. #include <windows.h>
  46. #include <windowsx.h>      // for GlobalFreePtr (JSC)
  47. #include "wvglob.h"
  48. #include "winvn.h"
  49. #pragma hdrstop
  50.  
  51. void setArticleFocus(HWND hWndArt);
  52. long NewCursorToTextLine (int X, int Y, TypDoc * DocPtr);
  53. long search_headers (TypDoc * HeaderDoc, header_p headers, long artindex, long num_headers);
  54. void SaveSelectedArts (HWND hwnd, TypDoc *Doc, header_p headers, long num_headers);
  55. long AffectSelected (TypDoc *Doc, BOOL value, BOOL compare);
  56. char_p string_compare_insensitive (char_p a, LPSTR b);
  57. void InitiateReceiveArticle (TypDoc *Doc, char far *articleId);
  58. BOOL article_operation (TypDoc * Doc,
  59.             long artindex,
  60.             BOOL (*art_fun) (header_p headers,
  61.                      TypGroup * group,
  62.                      long artindex));
  63.  
  64. BOOL toggle_read_unread (header_p headers, TypGroup * group, long artindex);
  65. BOOL toggle_selected (header_p headers, TypGroup * group, long artindex);
  66. BOOL selected_true (header_p headers, TypGroup * group, long artindex);
  67. BOOL selected_false (header_p headers, TypGroup * group, long artindex);
  68. BOOL seen_true (header_p headers, TypGroup * group, long artindex);
  69. BOOL seen_false (header_p headers, TypGroup * group, long artindex);
  70. BOOL mark_read_to_here (header_p headers, TypGroup * group, long artindex);
  71. BOOL mark_read_all (header_p headers, TypGroup * group, long artindex);
  72. void CloseGroupWnd(HWND hWnd, TypDoc *ThisDoc);
  73. void SetGroupMenus (HWND hWnd, int enable);
  74. void SetMenusForMultiArticleOperation (HWND hWnd, int enable);
  75.  
  76. /*--- FUNCTION: WinVnViewWndProc --------------------------------------------
  77.  *
  78.  *    Window procedure for a Group window, which contains the subjects
  79.  *    of the various articles in a newsgroup.
  80.  *    Note that there may be several different Group windows active;
  81.  *    this routine gets called any time anything happens to any of them.
  82.  */
  83.  
  84. long FAR PASCAL 
  85. WinVnViewWndProc (hWnd, message, wParam, lParam)
  86.      HWND hWnd;
  87.      UINT message;
  88.      WPARAM wParam;
  89.      LPARAM lParam;
  90. {
  91.  
  92.   PAINTSTRUCT ps;               /* paint structure          */
  93.  
  94.   HDC hDC;                      /* handle to display context */
  95.   RECT clientRect;              /* selection rectangle      */
  96.   TypDoc *ThisDoc;
  97.   int ih, j;
  98.   long artindex;
  99.   BOOL found;
  100.   long found_artindex;
  101.   int CtrlState;
  102.   TypBlock far *BlockPtr;
  103.   HANDLE hBlock;
  104.   unsigned int Offset;
  105.   BOOL continueFind;
  106.   char mybuf[MAXINTERNALLINE];
  107.   TypLineID MyLineID;
  108.   POINT ptCursor;
  109.   TypLine far * LinePtr;
  110.   int X, Y;
  111.   int OldSel = FALSE;
  112.  
  113.   /* We know what *window* is being acted on, but we must find
  114.    * out which *document* is being acted on.  There's a one-to-one
  115.    * relationship between the two, and we find out which document
  116.    * corresponds to this window by scanning the GroupDocs array.
  117.    */
  118.  
  119.   for (ih = 0, found = FALSE; !found && ih < MAXGROUPWNDS; ih++)
  120.     {
  121.       if (GroupDocs[ih].hDocWnd == hWnd)
  122.     {
  123.       found = TRUE;
  124.       ThisDoc = &(GroupDocs[ih]);
  125.     }
  126.     }
  127.  
  128.   if (!found)
  129.     {
  130.       ThisDoc = CommDoc;
  131.     }
  132.  
  133.   switch (message)
  134.     {
  135.     case WM_CREATE:
  136.       SetGroupMenus(hWnd, DISABLE);
  137.       NumGroupWnds++;
  138.       break;
  139.  
  140.     case WM_ACTIVATE:
  141.       if (wParam)
  142.     {
  143.       ActiveGroupDoc = ThisDoc;
  144.     }
  145.       /* fall through */
  146.     case WM_SYSCOMMAND:
  147.       return (DefWindowProc (hWnd, message, wParam, lParam));
  148.  
  149. /*   case WM_INITMENUPOPUP:                // (JSC) now handled by SetGroupMenus
  150.       EnableMenuItem(GetMenu(hWnd),IDM_MAIL,MailCtrl.enableMail) ;
  151.       break;
  152. */
  153.  
  154.     case WM_SIZE:
  155.       GetClientRect (hWnd, &clientRect);
  156.       ThisDoc->ScXWidth = clientRect.right;
  157.       ThisDoc->ScYHeight = clientRect.bottom;
  158.       ThisDoc->ScYLines = (clientRect.bottom - clientRect.top - TopSpace) / LineHeight;
  159.       ThisDoc->ScXChars = (clientRect.right - clientRect.left - SideSpace) / CharWidth;
  160.       break;
  161.  
  162.     case WM_CLOSE:
  163.       CloseGroupWnd(hWnd, ThisDoc);
  164.       break;
  165.  
  166.     case WM_DESTROY:
  167.       /* Unlink all the article windows that belong to this group */
  168.       NumGroupWnds--;
  169.       
  170.       LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
  171.         ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
  172.  
  173.       {
  174.     /* Clear the pointer in the line for this group in the   */
  175.     /* NetDoc document.  This pointer currently points       */
  176.     /* to the current document, which we are wiping out      */
  177.     /* with the destruction of this window.                  */
  178.     TypGroup far *group =
  179.       (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
  180.     long int CN_Total_Lines;
  181.  
  182.     group->SubjDoc = (TypDoc *) NULL;
  183.     
  184.       ThisDoc->InUse = FALSE;
  185.       if (ThisDoc == CommDoc)
  186.         {
  187.           CN_Total_Lines = CommDoc->TotalLines;
  188.           CommBusy = FALSE;
  189.           CommDoc = (TypDoc *) NULL;
  190.         }
  191.           /* SMR 940420 cause of mysterious F3 bug */
  192.             else {
  193.               CN_Total_Lines = group->total_headers;
  194.             }
  195.  
  196. /*CN    if (group->total_headers != 0) { */
  197.     if (CN_Total_Lines != 0) {    /* CN */
  198.  
  199.       UpdateSeenArts (ThisDoc);
  200.       UnlinkArtsInGroup (ThisDoc);
  201.       if (group->header_handle)
  202.         free_headers (group->header_handle, group->thread_handle);
  203.     
  204.       group->header_handle = (HANDLE) NULL;
  205.       group->thread_handle = (HANDLE) NULL; 
  206.       group->total_headers = 0;
  207.     }
  208.       }
  209.  
  210.       UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  211.       /* Clear document                                        */
  212.       FreeDoc (ThisDoc);
  213.  
  214.       /* If there's another group window, make it the active   */
  215.       /* group window so we don't create a new one if the      */
  216.       /* New Group flag is FALSE.                              */
  217.  
  218.       for (j = MAXGROUPWNDS - 1; j >= 0; j--)
  219.     {
  220.       if (GroupDocs[j].InUse)
  221.         {
  222.           ActiveGroupDoc = &(GroupDocs[j]);
  223.           break;
  224.         }
  225.     }
  226.       break;
  227.  
  228.     case WM_KEYDOWN:
  229.       /* See if this key should be mapped to a scrolling event
  230.        * for which we have programmed the mouse.  If so,
  231.        * construct the appropriate mouse call and call the mouse code.
  232.        */
  233.  
  234.       if (wParam == VK_F6)
  235.     {
  236.       NextWindow (ThisDoc);
  237.     }
  238.       else
  239.     {
  240.       CtrlState = GetKeyState (VK_CONTROL) < 0;
  241.       for (j = 0; j < NUMKEYS; j++)
  242.         {
  243.           if (wParam == key2scroll[j].wVirtKey &&
  244.           CtrlState == key2scroll[j].CtlState)
  245.         {
  246.           SendMessage (hWnd, key2scroll[j].iMessage,
  247.                    key2scroll[j].wRequest, 0L);
  248.           break;
  249.         }
  250.         }
  251.     }
  252.  
  253.       break;
  254.  
  255.  
  256.     case WM_CHAR:
  257.       /* Carriage Return means the same as double-clicking
  258.        * on where the cursor is currently pointing.
  259.        */
  260.       if (wParam == '\r')
  261.     {
  262.       GetCursorPos (&ptCursor);
  263.       ScreenToClient (hWnd, &ptCursor);
  264.       X = ptCursor.x;
  265.       Y = ptCursor.y;
  266.       goto getarticle;
  267.     }
  268.       else
  269.     {
  270.     }
  271.       break;
  272.  
  273.     case WM_RBUTTONDBLCLK:
  274.       /* double right click will mark all up to here as read */
  275.  
  276.       X = LOWORD (lParam);
  277.       Y = HIWORD (lParam);
  278.  
  279.       artindex = NewCursorToTextLine (X, Y, ThisDoc);
  280.     if (artindex > -1 )
  281.     {
  282.       article_operation (ThisDoc, artindex, mark_read_to_here);
  283.       InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);    
  284.     }
  285.  
  286.       break;
  287.  
  288.     case WM_MBUTTONDOWN:
  289.       /* Single middle click will toggle the read/unread status of the */
  290.       /* article */ 
  291.  
  292.       DragMouseAction = DRAG_NONE;
  293.       if (!Initializing)
  294.     {
  295.           BOOL status;
  296.           X = LOWORD (lParam);
  297.           Y = HIWORD (lParam);
  298.  
  299.           artindex = NewCursorToTextLine (X, Y, ThisDoc);
  300.       if (artindex > -1 )
  301.        {
  302.         status = article_operation (ThisDoc, artindex, toggle_read_unread);
  303.             DragMouseAction = status ? SEEN_SELECT : SEEN_DESELECT;
  304.         InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);      
  305.             SetCapture(hWnd);    // release capture on button up
  306.        }
  307.         }
  308.       break;
  309.  
  310.     case WM_LBUTTONDOWN:
  311.       /*  Clicking the left button on an article name toggles the
  312.        *  selected/not selected status of that group.
  313.        *  Currently selected groups are displayed in reverse video.
  314.        */
  315.  
  316.       DragMouseAction = DRAG_NONE;
  317.       if (!Initializing)
  318.     {
  319.           BOOL status;
  320.       X = LOWORD (lParam);
  321.       Y = HIWORD (lParam);
  322.  
  323.       artindex = NewCursorToTextLine (X, Y, ThisDoc);
  324.       if (artindex > -1 )
  325.         {
  326.              if (MK_SHIFT & wParam) 
  327.               {
  328.                status = article_operation(ThisDoc,artindex,toggle_read_unread);
  329.                DragMouseAction = status ? SEEN_SELECT : SEEN_DESELECT;
  330.           }
  331.              else
  332.            {
  333.                 status = article_operation(ThisDoc, artindex,toggle_selected);
  334.                 DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;
  335.            }
  336.              SetCapture(hWnd);    // release capture on button up
  337.          InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);
  338.         }
  339.     }
  340.       break;
  341.  
  342.     case WM_LBUTTONUP:
  343.       /*  Letting up on the left button on an article name 
  344.        *  gets us out of Dragging mode   */
  345.  
  346.       ReleaseCapture();
  347.       DragMouseAction = DRAG_NONE;
  348.       break;
  349.  
  350.     case WM_MBUTTONUP:
  351.       /*  Letting up on the middle button on an article name
  352.        *  gets us out of Dragging mode   */
  353.  
  354.       ReleaseCapture();
  355.       DragMouseAction = DRAG_NONE;
  356.       break;
  357.  
  358.     case WM_LBUTTONDBLCLK:
  359.       /*  Double-clicking on an article subject creates an "Article"
  360.        *  window, whose purpose is to display the article.
  361.        */
  362.       X = LOWORD (lParam);
  363.       Y = HIWORD (lParam);
  364.     getarticle:;
  365.  
  366.       artindex = NewCursorToTextLine (X, Y, ThisDoc);
  367.       if (artindex > -1 )
  368.     {
  369.       article_operation (ThisDoc, artindex, selected_false);
  370.       ViewArticle (ThisDoc, artindex, NO_REUSE, SHOW, NO_ID);
  371.     }
  372.  
  373.       break;
  374.  
  375.     case WM_MOUSEMOVE:
  376.       /*  Code to drag the mouse and change the select/not selected
  377.        *  status of that group.
  378.        */
  379.  
  380.       if ((!Initializing) && (DragMouseAction != DRAG_NONE))
  381.     {
  382.       X = LOWORD (lParam);
  383.       Y = HIWORD (lParam);
  384.  
  385.       artindex = NewCursorToTextLine (X, Y, ThisDoc);
  386.       if (artindex > -1 )
  387.         {
  388.                 switch (DragMouseAction)
  389.                   {
  390.                     case DRAG_SELECT:
  391.                 article_operation (ThisDoc, artindex, selected_true);
  392.                InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);
  393.                        break;
  394.  
  395.                     case DRAG_DESELECT:
  396.                      article_operation (ThisDoc, artindex, selected_false);
  397.                InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);
  398.                        break;
  399.  
  400.                     case SEEN_SELECT:
  401.                      article_operation (ThisDoc, artindex, seen_true);
  402.                InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);
  403.                        break;
  404.  
  405.                     case SEEN_DESELECT:
  406.                      article_operation (ThisDoc, artindex, seen_false);
  407.                InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);
  408.                        break;
  409.                   }
  410.  
  411.         }
  412.     }
  413.  
  414.       break;
  415.  
  416.     case WM_VSCROLL:
  417.       NewScrollIt (ThisDoc, wParam, lParam);
  418.       break;
  419.  
  420.     case WM_HSCROLL:
  421.       HScrollIt (ThisDoc, wParam, lParam);
  422.       break;
  423.  
  424.     case WM_PAINT:
  425.        {
  426.     HANDLE hBlock;
  427.     SIZE sz;
  428.     int MyLen;
  429.     unsigned int Offset;
  430.     int VertLines, HorzChars;
  431.     int EndofDoc = FALSE;
  432.     int RangeHigh, CurPos;
  433.     int indicatorwidth, Xtext;
  434.     char indicator;
  435.     unsigned int artindex;
  436.     long int artnum;
  437.     TypBlock far *NetBlockPtr;
  438.     TypLine far *NetLinePtr;
  439.     TypGroup far *group;
  440.     header_p headers;
  441.     header_p header;
  442.     HANDLE header_handle;
  443.     HANDLE thread_handle;   
  444.     char scratch_line [MAXINTERNALLINE];
  445.     char date_string [80];
  446.     unsigned long OldHighestSeen;
  447.     COLORREF MyColors[4], MyBack[4];
  448.     RECT aRect;
  449.     int MyColorMask = 0, PrevColorMask = MyColorMask;  
  450.     
  451.     /* MyColors and MyBack are arrays of colors used to display text
  452.      * foreground and background.
  453.      * The ColorMask variables are indices into these arrays.
  454.      * We set and clear bits in these indices depending upon
  455.      * whether the article has been selected or seen.
  456.      */
  457.  
  458. #define SEEN_MASK 1
  459. #define SELECT_MASK 2
  460.     hDC = BeginPaint (hWnd, &ps);
  461.  
  462.     GetClientRect (hWnd, &clientRect);
  463.     SelectObject (hDC, hListFont);
  464.  
  465.     VertLines = ( (ThisDoc->ScYLines > (ThisDoc->TotalLines - ThisDoc->TopLineOrd))
  466.              ? (ThisDoc->TotalLines - ThisDoc->TopLineOrd )
  467.              : ThisDoc->ScYLines);
  468.  
  469.     HorzChars = ThisDoc->ScXChars;
  470.  
  471.     MyColors[0] = ArticleUnSeenColor;    // unseen/unselected
  472.     MyColors[1] = ArticleSeenColor;        // seen/unselected
  473.     MyColors[2] = ListBackgroundColor;    // unseen/selected
  474.     MyColors[3] = MyColors[1];        // seen/selected
  475.  
  476.     MyBack[0] = MyColors[2];
  477.     MyBack[1] = MyColors[2];
  478.         if (ListBackgroundColor == RGB(0,0,0))
  479.            MyBack[2] = RGB(200,200,200);    // selected = soft/white background
  480.         else
  481.            MyBack[2] = RGB(0,0,0);        // selected = black background
  482.     MyBack[3] = MyBack[2];
  483.  
  484.         SetTextColor (hDC, MyColors[MyColorMask]);
  485.         SetBkColor (hDC, MyBack[MyColorMask]);
  486.  
  487.     /* Update the scroll bar thumb position.                 */
  488.  
  489.     CurPos = ThisDoc->TopLineOrd;
  490.     if (CurPos < 0)
  491.       CurPos = 0;
  492.     RangeHigh = ThisDoc->TotalLines - VertLines;
  493.     if (RangeHigh < 0)
  494.       RangeHigh = 0;
  495.     SetScrollRange (hWnd, SB_VERT, 0, RangeHigh, FALSE);
  496.     SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
  497.                        
  498.     RangeHigh = ThisDoc->LongestLine - ThisDoc->ScXChars; 
  499.     if (RangeHigh < 0) 
  500.     {
  501.       RangeHigh = 0;
  502.       ThisDoc->ScXOffset = 0;
  503.     }
  504.     SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
  505.     SetScrollPos (hWnd, SB_HORZ, ThisDoc->ScXOffset, TRUE); 
  506.     
  507.     GetTextExtentPoint(hDC, "s 99999 ", 8, &sz);                       
  508.     indicatorwidth = sz.cx;                                            
  509.     LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
  510.           &NetBlockPtr, &NetLinePtr);
  511.     group = (TypGroup far *) ((char far *) NetLinePtr + sizeof (TypLine));
  512.  
  513.     header_handle = group->header_handle;
  514.     thread_handle = group->thread_handle;
  515.  
  516.     if (header_handle)
  517.       headers = lock_headers (header_handle,thread_handle);
  518.  
  519.     OldHighestSeen = group->HighestPrevSeen;
  520.     UnlockLine (NetBlockPtr, NetLinePtr, &hBlock, &Offset, &MyLineID);
  521.  
  522.     /* Now paint this stuff on the screen.               */
  523.  
  524.     X = SideSpace - ThisDoc->ScXOffset * (CharWidth+2);
  525.     Xtext = X + indicatorwidth;
  526.     Y = StartPen;
  527.  
  528.     artindex = ThisDoc->TopLineOrd;
  529.  
  530.     if (ThisDoc->TotalLines)
  531.     do
  532.         {
  533.           header = header_elt (headers, artindex);
  534.  
  535.           artnum = header->number;
  536.  
  537.         indicator = ' ';
  538.         if(header->Seen) {
  539.             indicator = 's';
  540.         } else if(OldHighestSeen) {
  541.             if(header->number > OldHighestSeen) {
  542.                 indicator = 'n';
  543.             }
  544.         }
  545.  
  546.            if(ThisDoc->FindOffset == artindex)
  547.          indicator = '>';
  548.  
  549.           sprintf (scratch_line, "%c%5Flu %-5.5Fs %-18.18Fs %4Fd %-*s%-Fs ",
  550.                indicator,
  551.                header->number,
  552.                StringDate(date_string,header->date),
  553.                header->from,
  554.                header->lines,
  555.                header->thread_depth * 2,
  556.                "",
  557.                /* ack! */
  558.                (ThreadFullSubject || !(header->thread_depth))
  559.                ? header->subject
  560.                : "\020"
  561.                );
  562.  
  563.           MyLen = lstrlen (scratch_line);
  564.  
  565.           /* Figure out the color of this line.                 */
  566.  
  567.           MyColorMask = 0;
  568.           if (header->Seen)
  569.         {
  570.           MyColorMask |= SEEN_MASK;
  571.         }
  572.           else
  573.         {
  574.           MyColorMask &= (0xff - SEEN_MASK);
  575.         }
  576.           if (header->Selected)
  577.         {
  578.           MyColorMask |= SELECT_MASK;
  579.         }
  580.           else
  581.         {
  582.           MyColorMask &= 0xff - SELECT_MASK;
  583.         }
  584.           if (MyColorMask != PrevColorMask)
  585.         {
  586.           SetTextColor (hDC, MyColors[MyColorMask]);
  587.           SetBkColor (hDC, MyBack[MyColorMask]);
  588.           PrevColorMask = MyColorMask;
  589.         }
  590.  
  591.           /* Now write out the line.                            */
  592.  
  593.           SetRect (&aRect, 0, Y, clientRect.right, Y+LineHeight);
  594.           
  595.           ExtTextOut (hDC, X, Y, ETO_OPAQUE|ETO_CLIPPED, &aRect,
  596.                       scratch_line, MyLen, (LPINT)NULL);
  597.             
  598.           Y += LineHeight;
  599.           artindex++;
  600.  
  601.         }
  602.       while (--VertLines > 0 );
  603.  
  604.     if (header_handle)
  605.       unlock_headers (header_handle, thread_handle);
  606.  
  607.     /* We've reached the end of the data to be displayed     */
  608.     /* on this window.  If there's more screen real estate   */
  609.     /* left, just blank it out.                              */
  610.  
  611.         SelectObject (hDC, hListBackgroundBrush);
  612.     PatBlt (hDC, 0, Y, clientRect.right - 1, clientRect.bottom - Y, PATCOPY);
  613.  
  614.     EndPaint (hWnd, &ps);
  615.     break;
  616.       }
  617.  
  618.     case IDM_RETRIEVE_COMPLETE:
  619.       SetGroupMenus(hWnd, ENABLE);
  620.       break;
  621.  
  622.     case WM_COMMAND:
  623.       switch (LOWORD(wParam))
  624.     {
  625.     case IDV_EXIT:
  626.       CloseGroupWnd(hWnd, ThisDoc);
  627.       break;
  628.  
  629.     case IDV_NEXT:
  630.       break;
  631.  
  632.     case IDM_MARK_ALL:
  633.       article_operation (ThisDoc, 0, mark_read_all);
  634.       CloseGroupWnd(hWnd, ThisDoc);
  635.       break;
  636.     
  637.     case IDM_SORT_DATE:
  638.     case IDM_SORT_SUBJECT:
  639.     case IDM_SORT_LINES:
  640.     case IDM_SORT_THREADS:
  641.     case IDM_SORT_ARTNUM:
  642.     case IDM_SORT_FROM:
  643.       
  644.       { TypGroup far * group;
  645.         HANDLE header_handle,thread_handle;
  646.         header_p headers;
  647.         thread_array thread_index;
  648.         long i;
  649.  
  650.         LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
  651.               ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
  652.         group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  653.  
  654.         header_handle = group->header_handle;
  655.         thread_handle = group->thread_handle;
  656.  
  657.         /* this dependency on [thread_array_p][head0][head1][head2]... */
  658.         /* should be moved into headarry.c */
  659.  
  660.         headers = lock_headers (header_handle,thread_handle);
  661.         thread_index = * ((thread_array_p) ((char_p) headers - sizeof (char_p)));
  662.  
  663.         /* clear thread_depth info */
  664.         for (i=0;i<group->total_headers;i++)
  665.           headers[i].thread_depth = 0;
  666.  
  667.         UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  668.       
  669.         switch (LOWORD(wParam)) {
  670.         case IDM_SORT_DATE:
  671.           shell_sort_index_array (headers, thread_index,
  672.                       ThisDoc->TotalLines, compare_date);
  673.           break;
  674.  
  675.         case IDM_SORT_SUBJECT:
  676.           shell_sort_index_array (headers, thread_index,
  677.                       ThisDoc->TotalLines, compare_subject);
  678.           break;
  679.  
  680.         case IDM_SORT_LINES:
  681.           shell_sort_index_array (headers, thread_index,
  682.                       ThisDoc->TotalLines, compare_lines);
  683.           break;
  684.  
  685.         case IDM_SORT_THREADS:
  686.           if (threadp)
  687.         sort_by_threads (header_handle, thread_handle, ThisDoc->TotalLines);
  688.           else
  689.         MessageBox (hWnd, "Threading disabled", "WinVN", MB_OK);
  690.  
  691.           break;
  692.  
  693.         case IDM_SORT_ARTNUM:
  694.           shell_sort_index_array (headers, thread_index,
  695.                       ThisDoc->TotalLines, compare_artnum);
  696.           break;
  697.  
  698.         case IDM_SORT_FROM:
  699.           shell_sort_index_array (headers, thread_index,
  700.                       ThisDoc->TotalLines, compare_from);
  701.           break;
  702.         }
  703.         unlock_headers (header_handle, thread_handle);
  704.         InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);
  705.       }
  706.  
  707.       break;
  708.  
  709.     case IDM_FIND:
  710.     case IDM_FIND_NEXT_SAME:
  711.       FindDoc = ThisDoc;
  712.       continueFind = TRUE;
  713.       if (!FindDoc->SearchStr[0] || LOWORD(wParam) == IDM_FIND) {
  714.         if (!(FindDoc->SearchStr[0]) && LastArticleHeaderFind[0]) 
  715.           strcpy(FindDoc->SearchStr, LastArticleHeaderFind);
  716.         continueFind = DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
  717.       }
  718.       if (continueFind && FindDoc->SearchStr[0]) {
  719.         TypGroup far * group;
  720.         header_p headers;
  721.         HANDLE header_handle;
  722.         HANDLE thread_handle;           
  723.         int starting_at;
  724.  
  725.         LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
  726.               &BlockPtr, &LinePtr);
  727.         group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  728.  
  729.         header_handle = group->header_handle;
  730.         thread_handle = group->thread_handle;
  731.  
  732.         headers = lock_headers (header_handle,thread_handle);
  733.  
  734.         UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  735.  
  736.         /* 'Find Next' will start one line after */
  737.         starting_at = ThisDoc->TopLineOrd + ((LOWORD(wParam) == IDM_FIND) ? 0 : 1);
  738.  
  739.         /* back up one if we're at the end */
  740.         if (starting_at >= group->total_headers)
  741.           starting_at--;
  742.  
  743.         found_artindex = search_headers (ThisDoc, headers, starting_at, group->total_headers);
  744.         if (found_artindex == -1)
  745.           {
  746.         strcpy (mybuf, "\"");
  747.         strcat (mybuf, ThisDoc->SearchStr);
  748.         strcat (mybuf, "\" not found.");
  749.         MessageBox (hWnd, mybuf, "Not found", MB_OK);
  750.           }
  751.         else {
  752.           ThisDoc->TopLineOrd = (int) found_artindex;
  753.           ThisDoc->FindOffset = (int) found_artindex;
  754.             strcpy(LastArticleHeaderFind, FindDoc->SearchStr);
  755.           InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);
  756.         }
  757.  
  758.         unlock_headers (header_handle,thread_handle);
  759.       }
  760.       break;
  761.  
  762.     case IDM_POST:
  763.       /* We are creating the skeleton text of a new posting.
  764.        * Most of the work is done by CreatePostingWnd and
  765.        * CreatePostingText.  Here we have to identify
  766.        * the newsgroup for those routines.
  767.        * Get the newsgroup from the line in NetDoc that
  768.        * points to this document.
  769.        */
  770.       LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
  771.             ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
  772.       ExtractTextLine (ThisDoc->ParentDoc, LinePtr,
  773.                mybuf, MAXINTERNALLINE);
  774.       UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  775.       NewsgroupsPtr = mybuf;
  776.       CreatePostingWnd (hWnd, (TypDoc *) NULL, DOCTYPE_POSTING);
  777.       break;
  778.  
  779.     case IDM_SAVE_SELECTED:
  780.       /* Query user for file name */
  781.       if (!DialogBox (hInst, "WinVnSaveArts", hWnd, lpfnWinVnSaveArtsDlg)) {
  782.          InvalidateRect (hWnd, NULL, TRUE);
  783.       } else {
  784.          numArtsSaved = 0;
  785.          savingArtIndex = 0;
  786.          SetMenusForMultiArticleOperation (hWnd, DISABLE);
  787.  
  788.          goto doretrieve;
  789.      }
  790.      break;
  791.  
  792.     case IDM_DECODE_SELECTED:
  793.       /* Query user for file name */
  794.       if (!DialogBoxParam (hInst, "WinVnDecodeArts", hWnd, lpfnWinVnDecodeArtsDlg, 1)) {
  795.          InvalidateRect (hWnd, NULL, TRUE);
  796.       } else {
  797.          if (TestCommBusy(hWnd, "Can't decode selected articles"))
  798.            break;
  799.          if (TestDecodeBusy(hWnd, "Can't decode selected articles"))
  800.            break;
  801.            
  802.          DecodeInit();
  803.          CommDoc = ThisDoc;
  804.          CommDecoding = TRUE;
  805.          numArtsSaved = 0;
  806.          savingArtIndex = 0;
  807.          SetMenusForMultiArticleOperation (hWnd, DISABLE);
  808.  
  809.          goto doretrieve;
  810.      }
  811.      break;
  812.  
  813.     case IDM_SELECT_ALL:
  814.       /* select all articles */   
  815.       AffectSelected (ThisDoc, SELECT, NO_COMPARE);
  816.     break;
  817.  
  818.     case IDM_DESELECT_ALL:
  819.       /* deselect all articles */   
  820.        AffectSelected (ThisDoc, DESELECT, NO_COMPARE);
  821.     break;
  822.  
  823.     case IDM_SELECT_MATCH:
  824.       /* select all articles containing a string*/
  825.       FindDoc = ThisDoc;   
  826.       DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
  827.       if(strcmp(FindDoc->SearchStr,"")) {
  828.         if (AffectSelected (FindDoc, SELECT, COMPARE)==0) {
  829.         strcpy (mybuf, "\"");
  830.         strcat (mybuf, FindDoc->SearchStr);
  831.         strcat (mybuf, "\" not found.");
  832.         MessageBox (hWnd, mybuf, "Not found", MB_OK);
  833.         }
  834.       }
  835.       break;
  836.  
  837.     case IDM_MAIL:
  838.       (MailCtrl.fnMlWinCreate)(hWnd, (TypDoc *) NULL, DOCTYPE_MAIL);
  839.       break;
  840.  
  841.     case IDM_UPDATE:
  842.       if (!CommBusy) {
  843.         TypGroup far * group;
  844.         char far * group_name;
  845.  
  846.         /* update the newsrc data */
  847.         UpdateSeenArts (ThisDoc);
  848.         LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
  849.               ThisDoc->ParentLineID,&BlockPtr, &LinePtr);
  850.         group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  851.         group_name = (char far *) LinePtr
  852.           + sizeof (TypLine) + sizeof (TypGroup);
  853.  
  854.         // Free whatever headers are there.
  855.         if (group->header_handle)
  856.           free_headers (group->header_handle, group->thread_handle);
  857.  
  858.         group->header_handle = (HANDLE) NULL;
  859.         group->thread_handle = (HANDLE) NULL;
  860.         
  861.         // zero out the received line count
  862.         RcvLineCount = 0;
  863.  
  864.         // capture the mouse to the usenet window, so that we keep
  865.         // the hourglass.
  866.         SetCapture (hWndConf);
  867.  
  868.         SetGroupMenus(hWnd, DISABLE);
  869.  
  870.         CommDoc = ThisDoc;
  871.         CommState = ST_GROUP_RESP;
  872.         strcpy (mybuf, "GROUP ");
  873.         lstrcat (mybuf, group_name);
  874.         PutCommLine (mybuf);
  875.       }
  876.       break;
  877.  
  878.       }
  879.       break;
  880.  
  881.     case IDM_ARTICLE_RETRIEVE_COMPLETE:
  882.     {
  883.       TypGroup far * group;
  884.  
  885.       header_p headers;
  886.       HANDLE header_handle;
  887.       HANDLE thread_handle;
  888.           int justSavedIndex;
  889.           
  890.       if (savingArtIndex == -1)       /* this retrieve wasn't part of a save operation */
  891.          break;
  892.  
  893.       if (CodingState == INACTIVE)
  894.       {
  895.         /* If this is the 1st article saved, then value of Append depends
  896.          * on what the user selected in dialog.  If > 1st, always append 
  897.          */
  898.         if (!MRRWriteDocument (ActiveArticleDoc, sizeof (TypText),
  899.                        SaveArtFileName,
  900.                        (numArtsSaved==0)?SaveArtAppend:TRUE))
  901.           {
  902.                  MessageBox (hWnd, "Could not write to file.  Operation canceled", "Problems saving file", MB_OK | MB_ICONEXCLAMATION);
  903.             SetMenusForMultiArticleOperation (hWnd, ENABLE);
  904.                  savingArtIndex = -1;
  905.                  break;
  906.           }
  907.       } else {
  908.                 if (CompleteThisDecode() == FAIL)
  909.         {
  910.             DecodeDone ();                    
  911.                  MessageBox (hWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
  912.             SetMenusForMultiArticleOperation (hWnd, ENABLE);
  913.                  savingArtIndex = -1;
  914.                     break;
  915.                 }
  916.       }
  917.  
  918.       justSavedIndex = savingArtIndex;
  919.       numArtsSaved++;
  920.       savingArtIndex++;
  921.  
  922.     doretrieve:;
  923.       LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
  924.               &BlockPtr, &LinePtr);
  925.       group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  926.       header_handle = group->header_handle;
  927.       thread_handle = group->thread_handle;
  928.       headers = lock_headers (header_handle,thread_handle);
  929.       UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  930.       
  931.       if (numArtsSaved > 0)        // deselect the article just finished
  932.         (header_elt(headers, justSavedIndex))->Selected = FALSE;
  933.     
  934.       /* skip to next selected article */
  935.       while (savingArtIndex < group->total_headers &&
  936.              (header_elt(headers, savingArtIndex))->Selected == FALSE)
  937.          savingArtIndex++;
  938.         
  939.       if (savingArtIndex >= group->total_headers)    /* done */
  940.       {
  941.         if (numArtsSaved > 0) {
  942.             if (CodingState == INACTIVE)
  943.         {
  944.             sprintf (mybuf, "Saved %d article%c to file %s", 
  945.                 numArtsSaved, (numArtsSaved==1)?' ':'s',SaveArtFileName);
  946.             MessageBox (hWnd, mybuf, "Done", MB_OK);
  947.         }
  948.         } else
  949.         MessageBox (hWnd, "No articles selected", "Done", MB_OK);
  950.           
  951.             if (CodingState != INACTIVE)
  952.         DecodeDone();
  953.  
  954.         SetMenusForMultiArticleOperation (hWnd, ENABLE);
  955.  
  956. //      (JSC) now deselect article as soon as it's successfully finished 
  957. //        for (savingArtIndex=0; savingArtIndex < group->total_headers; savingArtIndex++ )
  958. //        (header_elt(headers, savingArtIndex))->Selected = FALSE;
  959.  
  960.         unlock_headers (header_handle,thread_handle);
  961.           savingArtIndex = -1;        /* reset to non-saving state */
  962.       }
  963.       else
  964.       {
  965.         if (KeepArticleHeaderVisible)
  966.             AdjustTopScByDoc (ThisDoc, savingArtIndex);
  967.             
  968.         sprintf(mybuf, "%ld", (header_elt(headers, savingArtIndex))->number);
  969.           (header_elt(headers, savingArtIndex))->Seen = TRUE;
  970.         unlock_headers (header_handle, thread_handle);
  971.         if (CodingState != INACTIVE)
  972.         { 
  973.           if ((currentCoded = InitCoded(hWnd)) == NULL)
  974.           {
  975.             MessageBox (hWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
  976.             DecodeDone();
  977.             SetMenusForMultiArticleOperation (hWnd, ENABLE);
  978.             savingArtIndex = -1;
  979.             break;
  980.           }
  981.           InitiateReceiveArticle (ThisDoc, mybuf);
  982.           UpdateBlockStatus ();    // display some initial status
  983.         } else 
  984.                 ViewArticle (ThisDoc, savingArtIndex, REUSE, NO_SHOW, NO_ID);
  985.       }
  986.            InvalidateRect (hWnd, NULL, FALSE);
  987.       break;
  988.     }    
  989.        
  990.     default:
  991.       return (DefWindowProc (hWnd, message, wParam, lParam));
  992.     }
  993.   return (0);
  994. }             
  995.  
  996. /*------------ CloseGroupWnd ------------------------------
  997.  *
  998.  *  Make sure this Wnd is not the active Comm window, then destroy it
  999.  */
  1000. void CloseGroupWnd(HWND hWnd, TypDoc *ThisDoc)
  1001. {
  1002.       if (CommBusy && ThisDoc == CommDoc)
  1003.      MessageBox (hWnd,
  1004.       "Please wait until group activity is complete",
  1005.       "Cannot close group window", MB_OK|MB_ICONSTOP);
  1006.       else
  1007.         DestroyWindow (hWnd);
  1008. }
  1009.  
  1010. /*------------ SetGroupMenus ------------------------------
  1011.  * dis/enable menu items which depend on group list being completely 
  1012.  * retrieved
  1013.  */
  1014. void 
  1015. SetGroupMailMenu(HWND hWnd)
  1016. {
  1017.     HMENU hMenu, hSubMenu;
  1018.  
  1019.     hMenu = GetMenu (hWnd);
  1020.     hSubMenu = GetSubMenu (hMenu, 0);    // Articles menu
  1021.     EnableMenuItem (hSubMenu, IDM_MAIL, MailCtrl.enableMail);
  1022. }
  1023.  
  1024. void
  1025. SetGroupMenus (HWND hWnd, int enable)    
  1026. {
  1027.     HMENU hMenu, hSubMenu;
  1028.     UINT mode;
  1029.     
  1030.     if (enable == ENABLE)    
  1031.         mode = MF_BYCOMMAND|MF_ENABLED;
  1032.     else
  1033.         mode = MF_BYCOMMAND|MF_DISABLED|MF_GRAYED;
  1034.     
  1035.     hMenu = GetMenu (hWnd);
  1036.     hSubMenu = GetSubMenu (hMenu, 0);    // Articles menu
  1037.     EnableMenuItem (hSubMenu, IDM_UPDATE, mode);
  1038.     EnableMenuItem (hSubMenu, IDM_SAVE_SELECTED, mode);
  1039.     EnableMenuItem (hSubMenu, IDM_DECODE_SELECTED, mode);
  1040.     EnableMenuItem (hSubMenu, IDM_SELECT_ALL, mode);
  1041.     EnableMenuItem (hSubMenu, IDM_DESELECT_ALL, mode);
  1042.     EnableMenuItem (hSubMenu, IDM_SELECT_MATCH, mode);
  1043.     EnableMenuItem (hSubMenu, IDM_MARK_ALL, mode);
  1044.     EnableMenuItem (hSubMenu, IDV_EXIT, mode);
  1045.     SetGroupMailMenu(hWnd);
  1046.     
  1047.     hSubMenu = GetSubMenu (hMenu, 1);    // Sort menu
  1048.     EnableMenuItem (hSubMenu, IDM_SORT_DATE, mode);
  1049.     EnableMenuItem (hSubMenu, IDM_SORT_SUBJECT, mode);
  1050.     EnableMenuItem (hSubMenu, IDM_SORT_LINES, mode);
  1051.     EnableMenuItem (hSubMenu, IDM_SORT_THREADS, mode);
  1052.     EnableMenuItem (hSubMenu, IDM_SORT_ARTNUM, mode);
  1053.     EnableMenuItem (hSubMenu, IDM_SORT_FROM, mode);
  1054.  
  1055.     hSubMenu = GetSubMenu (hMenu, 2);    // Search menu
  1056.     EnableMenuItem (hSubMenu, IDM_FIND, mode);
  1057.     EnableMenuItem (hSubMenu, IDM_FIND_NEXT_SAME, mode);
  1058. }
  1059.  
  1060. /*------------ SetMenusForMultiArticleOperation ----------
  1061.  *  (JSC) killer function name, huh?!
  1062.  * During multi-article op (i.e. Save selected, Decode selected)
  1063.  * we don't want the user to do any of these things
  1064.  */
  1065. void
  1066. SetMenusForMultiArticleOperation (HWND hWnd, int enable)
  1067. {
  1068.     HMENU hMenu, hSubMenu;
  1069.     UINT mode;
  1070.     
  1071.     if (enable == ENABLE)    
  1072.         mode = MF_BYCOMMAND|MF_ENABLED;
  1073.     else
  1074.         mode = MF_BYCOMMAND|MF_DISABLED|MF_GRAYED;
  1075.     
  1076.     hMenu = GetMenu (hWnd);
  1077.     hSubMenu = GetSubMenu (hMenu, 0);    // Articles menu
  1078.     EnableMenuItem (hSubMenu, IDM_UPDATE, mode);
  1079.     EnableMenuItem (hSubMenu, IDM_SAVE_SELECTED, mode);
  1080.     EnableMenuItem (hSubMenu, IDM_DECODE_SELECTED, mode);
  1081.     EnableMenuItem (hSubMenu, IDV_EXIT, mode);
  1082.     EnableMenuItem (hSubMenu, IDM_MARK_ALL, mode);
  1083.  
  1084.     hSubMenu = GetSubMenu (hMenu, 1);    // Sort menu
  1085.     EnableMenuItem (hSubMenu, IDM_SORT_DATE, mode);
  1086.     EnableMenuItem (hSubMenu, IDM_SORT_SUBJECT, mode);
  1087.     EnableMenuItem (hSubMenu, IDM_SORT_LINES, mode);
  1088.     EnableMenuItem (hSubMenu, IDM_SORT_THREADS, mode);
  1089.     EnableMenuItem (hSubMenu, IDM_SORT_ARTNUM, mode);
  1090.     EnableMenuItem (hSubMenu, IDM_SORT_FROM, mode);
  1091. }
  1092.  
  1093. /* --- Function FileLength -------------------------------------------
  1094.  *
  1095.  *    Find the size, in bytes, of a file.
  1096.  *
  1097.  *    Entry    hFile    handle of the file in question.
  1098.  *
  1099.  *    Exit     returns the length of the file in bytes.
  1100.  *
  1101.  *    This routine is no longer used.
  1102.  */
  1103.  
  1104. long
  1105. FileLength (hFile)
  1106.      HFILE hFile;
  1107. {
  1108.   long lCurrentPos = _llseek (hFile, 0L, 1);
  1109.   long lFileLength = _llseek (hFile, 0L, 2);
  1110.  
  1111.   _llseek (hFile, lCurrentPos, 0);
  1112.  
  1113.   return lFileLength;
  1114.  
  1115. }
  1116.  
  1117. BOOL
  1118. article_operation (TypDoc * Doc,
  1119.            long artindex,
  1120.            BOOL (*art_fun) (header_p headers,
  1121.                     TypGroup * group,
  1122.                     long artindex))
  1123. {
  1124.  
  1125.   HANDLE hBlock;
  1126.   unsigned int Offset;
  1127.   TypLineID MyLineID;
  1128.   TypBlock far *BlockPtr;
  1129.   TypLine far *LinePtr;
  1130.   TypGroup * GroupDoc;
  1131.   HANDLE header_handle;
  1132.   HANDLE thread_handle;
  1133.   BOOL result = FALSE;
  1134.   header_p headers;
  1135.  
  1136.   LockLine (Doc->hParentBlock, Doc->ParentOffset,
  1137.         Doc->ParentLineID, &BlockPtr, &LinePtr);
  1138.  
  1139.   GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  1140.  
  1141.   header_handle = GroupDoc->header_handle;
  1142.   if (header_handle) {
  1143.  
  1144.     thread_handle = GroupDoc->thread_handle;
  1145.     headers = lock_headers (header_handle,thread_handle);
  1146.  
  1147.     result = art_fun(headers, GroupDoc, artindex);
  1148.  
  1149.     unlock_headers (header_handle,thread_handle);
  1150.     UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  1151.   }
  1152.   return (result);
  1153. }    
  1154.  
  1155. BOOL
  1156. toggle_read_unread (header_p headers,
  1157.             TypGroup * GroupDoc,
  1158.             long artindex)
  1159. {
  1160.  
  1161.   /* only try if in range.  All this work for these two lines 8^) */
  1162.   if ((artindex > -1 ) && (artindex < GroupDoc->total_headers)) {
  1163.     header_p header = header_elt (headers, artindex);
  1164.     header->Seen = ( header->Seen ? FALSE : TRUE );
  1165.     return (header->Seen);
  1166.   }
  1167.   return(FALSE);
  1168. }
  1169.  
  1170. BOOL
  1171. mark_read_to_here (header_p headers,
  1172.            TypGroup * GroupDoc,
  1173.            long artindex)
  1174. {
  1175.   header_p header;
  1176.  
  1177.   /* only try if in range.  All this work for these two lines 8^) */
  1178.   if ((artindex > -1 ) && (artindex < GroupDoc->total_headers)) {
  1179.     int i;
  1180.     for (i = 0; i <= artindex; i++) {
  1181.       header = header_elt (headers, i);
  1182.       header->Seen = TRUE;
  1183.     }
  1184.     return(TRUE);
  1185.   }
  1186.  return(FALSE);
  1187. }
  1188.  
  1189. BOOL
  1190. mark_read_all (header_p headers,
  1191.            TypGroup * GroupDoc,
  1192.            long artindex)
  1193. {
  1194.   header_p header; 
  1195.  
  1196.     int i;
  1197.     for (i = 0; i < GroupDoc->total_headers; i++) {
  1198.       header = header_elt (headers, i);
  1199.       header->Seen = TRUE;
  1200.     }
  1201.     return(TRUE);
  1202. }
  1203.  
  1204. BOOL
  1205. toggle_selected (header_p headers,
  1206.          TypGroup * GroupDoc,
  1207.          long artindex)
  1208. {
  1209.  
  1210.   /* only try if in range.  All this work for these two lines 8^) */
  1211.   if ((artindex > -1 ) && (artindex < GroupDoc->total_headers)) {
  1212.     header_p header = header_elt (headers, artindex);
  1213.     header->Selected = ( header->Selected ? FALSE : TRUE );
  1214.     return(header->Selected);
  1215.   }
  1216.  return(FALSE);
  1217. }
  1218.  
  1219. BOOL
  1220. seen_false (header_p headers,
  1221.          TypGroup * GroupDoc,
  1222.          long artindex)
  1223. {
  1224.  
  1225.   /* only try if in range. */
  1226.   if ((artindex > -1 ) && (artindex < GroupDoc->total_headers)) {
  1227.     header_p header = header_elt (headers, artindex);
  1228.     header->Seen = FALSE;
  1229.     return(TRUE);
  1230.   }
  1231.  return(FALSE);
  1232. }
  1233.  
  1234. BOOL
  1235. seen_true (header_p headers,
  1236.          TypGroup * GroupDoc,
  1237.          long artindex)
  1238. {
  1239.  
  1240.   /* only try if in range. */
  1241.   if ((artindex > -1 ) && (artindex < GroupDoc->total_headers)) {
  1242.     header_p header = header_elt (headers, artindex);
  1243.     header->Seen = TRUE;
  1244.     return(TRUE);
  1245.   }
  1246.  return(FALSE);
  1247. }
  1248.  
  1249. BOOL
  1250. selected_true (header_p headers,
  1251.          TypGroup * GroupDoc,
  1252.          long artindex)
  1253. {
  1254.  
  1255.   /* only try if in range. */
  1256.   if ((artindex > -1 ) && (artindex < GroupDoc->total_headers)) {
  1257.     header_p header = header_elt (headers, artindex);
  1258.     header->Selected = TRUE;
  1259.     return(TRUE);
  1260.   }
  1261.  return(FALSE);
  1262. }
  1263.  
  1264. BOOL
  1265. selected_false (header_p headers,
  1266.          TypGroup * GroupDoc,
  1267.          long artindex)
  1268. {
  1269.  
  1270.   /* only try if in range. */
  1271.   if ((artindex > -1 ) && (artindex < GroupDoc->total_headers)) {
  1272.     header_p header = header_elt (headers, artindex);
  1273.     header->Selected = FALSE;
  1274.     return(TRUE);
  1275.   }
  1276.  return(FALSE);
  1277. }
  1278.  
  1279.  
  1280. /*-- function ViewArticle -------------------------------------------------
  1281.  *
  1282.  *  View a given article.   Either create a new window for it or
  1283.  *  recycle an existing window.
  1284.  *  This function requests an article from the server, so there
  1285.  *  must not already be a transaction in progress.
  1286.  *  Can retrieve an article by artindex from the group header list,
  1287.  *  or any arbitrary article ID.
  1288.  *
  1289.  *    Entry    Doc            points to the document for this group.
  1290.  *             artindex       index into header array for this group.
  1291.  *             Reuse          is TRUE if we ought to reuse the
  1292.  *                            currently active article window (if any).
  1293.  *             showArt        is TRUE to retrieve maximized (shown)
  1294.  *                            is FALSE to retrieve minimized
  1295.  *             articleId      is NO_ID if wish to retrieve artindex->number
  1296.  *                            is some art ID if wish to retrieve article by ID
  1297.  */
  1298. void
  1299. ViewArticle (Doc, artindex, Reuse, showArt, articleId)
  1300.      TypDoc *Doc;
  1301.      long artindex;
  1302.      BOOL Reuse;
  1303.      BOOL showArt;
  1304.      char far *articleId;
  1305.   TypDoc *MyDoc;
  1306.   TypGroup far *GroupDoc;
  1307.   BOOL newdoc;
  1308.   BOOL found;
  1309.   int docnum;
  1310.   TypBlock far *BlockPtr;
  1311.   TypLine far *LinePtr;
  1312.   HWND hWndArt;
  1313.   int x,y,width,height;
  1314.   char mybuf[MAXINTERNALLINE];
  1315.   char far *lpsz;
  1316.   HWND hWndGroup = Doc->hDocWnd;
  1317.   HANDLE header_handle;
  1318.   HANDLE thread_handle;
  1319.   header_p headers;
  1320.   header_p header;
  1321.  
  1322.   
  1323.   LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
  1324.         &BlockPtr, &LinePtr);
  1325.  
  1326.   GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  1327.  
  1328.   
  1329.    if (articleId == NO_ID)   /* if retrieving artindex, only try if in range */
  1330.       if ((artindex < 0) || (artindex >= GroupDoc->total_headers))
  1331.          return;
  1332.  
  1333.   header_handle = GroupDoc->header_handle;
  1334.   thread_handle = GroupDoc->thread_handle;
  1335.  
  1336.   headers = lock_headers (header_handle,thread_handle);
  1337.   header = header_elt(headers, artindex);
  1338.  
  1339.   if (articleId == NO_ID && (MyDoc = header->ArtDoc))
  1340.     {
  1341.       /* We already have a document containing the article */
  1342.       /* so just activate it.                */
  1343.  
  1344.       if (showArt)
  1345.       {
  1346.      setArticleFocus( MyDoc->hDocWnd );
  1347.          ShowWindow(MyDoc->hDocWnd,SW_SHOWNORMAL); 
  1348.       } else
  1349.          ShowWindow(MyDoc->hDocWnd,SW_SHOWMINIMIZED);
  1350.  
  1351.       InvalidateRect (MyDoc->hDocWnd, NULL, FALSE);
  1352.       SendMessage(hWndGroup, IDM_ARTICLE_RETRIEVE_COMPLETE, 0, 0);
  1353.       goto endit;
  1354.     }
  1355.   if (TestCommBusy(hWndGroup, "Can't request text of article"))
  1356.      goto endit;
  1357.   newdoc = FALSE;
  1358.   if ((NewArticleWindow && !Reuse) || !ActiveArticleDoc || !(ActiveArticleDoc->InUse))
  1359.     {
  1360.       found = FALSE;
  1361.       for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++)
  1362.     {
  1363.       if (!ArticleDocs[docnum].InUse)
  1364.         {
  1365.           found = TRUE;
  1366.           newdoc = TRUE;
  1367.           CommDoc = &(ArticleDocs[docnum]);
  1368.           break;
  1369.         }
  1370.     }
  1371.       if (!found)
  1372.     {
  1373.       MessageBox (hWndGroup,
  1374.               "You have too many article windows active;\n"
  1375.               "Close one or uncheck the option"
  1376.               "'New Window for each Article'.",
  1377.               "Can't open new window", MB_OK | MB_ICONASTERISK);
  1378.       goto endit;
  1379.     }
  1380.     }
  1381.   else
  1382.     {
  1383.       /* Must reuse old window for this article.         */
  1384.       ActiveArticleDoc->LongestLine = 0;
  1385.       ActiveArticleDoc->ScXOffset = 0;
  1386.       ActiveArticleDoc->TextSelected = FALSE;
  1387.       EnableMenuItem(GetMenu(ActiveArticleDoc->hDocWnd),IDM_COPY,MF_GRAYED) ;
  1388.       EnableMenuItem(GetMenu(ActiveArticleDoc->hDocWnd),IDM_DESELECT_ALL,MF_GRAYED) ;
  1389.       CommDoc = ActiveArticleDoc;
  1390.       /* sever the article/artindex connection */
  1391.       if (CommDoc->ParentDoc == Doc)        // (JSC) is this check really necessary?
  1392.        (header_elt (headers,CommDoc->ParentOffset))->ArtDoc = (TypDoc *) NULL;
  1393.  
  1394.       /* clear out old doc */
  1395.       FreeDoc (CommDoc);
  1396.     }
  1397.  
  1398.   header->Seen = TRUE;
  1399.   InvalidateRect (hWndGroup, NULL, FALSE);
  1400.  
  1401.   if (articleId == NO_ID)
  1402.      lpsz = (char far *) header->subject;
  1403.   else
  1404.      lpsz = articleId;
  1405.  
  1406.   strcpy (mybuf, "Retrieving \"");
  1407.   lstrcat (mybuf, lpsz);
  1408.   lstrcat (mybuf, "\"");
  1409.  
  1410.   if (newdoc)
  1411.     {
  1412.       char poschars[MAXINTERNALLINE];
  1413.  
  1414.       /* Compute default screen position. */
  1415.       if (xScreen > 88 * ArtCharWidth) {
  1416.     width = 88 * ArtCharWidth;
  1417.       } else {
  1418.     width = xScreen - 1 * ArtCharWidth;
  1419.       }
  1420.       x = xScreen - width;
  1421.       y = (int) (yScreen * 3 / 8);
  1422.       height = (int) (yScreen * 5 / 8) - (1 * ArtLineHeight);
  1423.  
  1424.       /* If the screen position has been saved, use that instead. */
  1425.       GetPrivateProfileString (szAppName, "ArticleWindowPos", "!",
  1426.                    poschars,MAXINTERNALLINE,szAppProFile);
  1427.       if(poschars[0] != '!') {
  1428.     sscanf(poschars,"%d,%d,%d,%d",&x,&y,&width,&height);
  1429.       }
  1430.  
  1431.       hWndArt = CreateWindow ("WinVnArt",
  1432.                   mybuf,
  1433.                   WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
  1434.                   /* Initial X pos */
  1435.                   x - (docnum * ArtCharWidth),
  1436.                   /* Initial Y pos */
  1437.                   y + docnum * ArtLineHeight,
  1438.                   width, /* Initial X Width */
  1439.                   /* Initial Y height */
  1440.                   height,
  1441.                   NULL,
  1442.                   NULL,
  1443.                   hInst,
  1444.                   NULL);
  1445.  
  1446.       if (!hWndArt)      
  1447.     return;                 /* ??? */
  1448.  
  1449.       SetHandleBkBrush (hWndArt, hArticleBackgroundBrush);
  1450.       ShowWindow (hWndArt, SW_SHOWNORMAL);
  1451.     }
  1452.   else
  1453.     {
  1454.       hWndArt = CommDoc->hDocWnd;               
  1455.       SetWindowText (hWndArt, mybuf);
  1456.     }
  1457.  
  1458.   /*  Now that we have created the window, create the corresponding
  1459.    *  document, and make the new window active.
  1460.    */
  1461.  
  1462.   InitDoc (CommDoc, hWndArt, Doc, DOCTYPE_ARTICLE);
  1463.  
  1464.   CommDoc->InUse = TRUE;
  1465.   CommDoc->LastSeenLineID = artindex;   /* Keep an index with the article */
  1466.  
  1467.   if (showArt)
  1468.   {
  1469.     setArticleFocus( hWndArt) ;
  1470.     ShowWindow (hWndArt, SW_SHOWNORMAL);
  1471.   } else
  1472.     ShowWindow (hWndArt, SW_SHOWMINIMIZED);
  1473.  
  1474.   header->ArtDoc = CommDoc;
  1475.   CommDoc->ParentOffset = (int) artindex;   /* should this be iff articleId==NO_ID ? */
  1476.   InvalidateRect (hWndArt, NULL, FALSE);
  1477.   UpdateWindow (hWndArt);
  1478.  
  1479.   if (articleId == NO_ID)
  1480.   {
  1481.     sprintf(mybuf, "%ld", header->number);
  1482.     InitiateReceiveArticle (Doc, mybuf);
  1483.   }
  1484.   else
  1485.     InitiateReceiveArticle (Doc, articleId);
  1486.   
  1487.  endit:
  1488.   unlock_headers (header_handle, thread_handle);
  1489.   
  1490. }
  1491.  
  1492. void
  1493. setArticleFocus(HWND hWndArt) 
  1494. {
  1495.   SetArticleRot13Mode(hWndArt,FALSE) ;
  1496.   SetActiveWindow (hWndArt);
  1497.   SetFocus (hWndArt);
  1498. }
  1499.  
  1500. void
  1501. InitiateReceiveArticle (TypDoc *Doc, char far *articleId)
  1502. {
  1503.   TypBlock far *BlockPtr;
  1504.   TypLine far *LinePtr;
  1505.   char far *lpszGroupName;
  1506.   char mybuf[MAXINTERNALLINE];
  1507.   HANDLE hBlock;
  1508.   unsigned int Offset;
  1509.   TypLineID MyLineID;
  1510.  
  1511.   CommLinePtr = CommLineIn;
  1512.   CommBusy = TRUE;
  1513.   CommState = ST_ARTICLE_RESP;
  1514.  
  1515.   LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
  1516.         &BlockPtr, &LinePtr);
  1517.  
  1518.   /* If we're not already in this group on the server,
  1519.    * send out a GROUP command for this window so we get back
  1520.    * into the right Group.
  1521.    */
  1522.   lpszGroupName = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
  1523.   if (lstrcmp (CurrentGroup, lpszGroupName))
  1524.     {
  1525.       CommState = ST_GROUP_REJOIN;
  1526.       strcpy (mybuf, "GROUP ");
  1527.       lstrcat (mybuf, lpszGroupName);
  1528.       mylstrncpy (CurrentGroup, lpszGroupName, MAXGROUPNAME);
  1529.       PutCommLine (mybuf);
  1530.     }
  1531.  
  1532.   UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  1533.  
  1534.   sprintf (mybuf, "ARTICLE %s", articleId);
  1535.   PutCommLine (mybuf);
  1536. }
  1537.  
  1538.  
  1539. /*-- Function UnlinkArtsInGroup ---------------------------------------
  1540.  *
  1541.  *  Modify all the article documents and all the article windows currently
  1542.  *  associated with a group so that none of them points to that group.
  1543.  *  Used when the group window is going away or is being recycled.
  1544.  *
  1545.  *    Entry    GroupDoc    points to the document to which references
  1546.  *                         should be eliminated.
  1547.  */
  1548. void
  1549. UnlinkArtsInGroup (GroupDoc)
  1550.      TypDoc *GroupDoc;
  1551. {
  1552.   int iart;
  1553.  
  1554.   for (iart = 0; iart < MAXARTICLEWNDS; iart++)
  1555.     {
  1556.       if (ArticleDocs[iart].InUse && ArticleDocs[iart].ParentDoc == GroupDoc)
  1557.     {
  1558.       ArticleDocs[iart].ParentDoc = (TypDoc *) NULL;
  1559.       ArticleDocs[iart].hParentBlock = 0;
  1560.     }
  1561.     }
  1562. }
  1563.  
  1564. /*--- function UpdateSeenArts -------------------------------------------
  1565.  *
  1566.  *  Given a Group document, update the TypGroup line for
  1567.  *  that document in the Net document with respect to which
  1568.  *  articles have been seen.
  1569.  *  This routine would typically be called just before a Group document
  1570.  *  is going to be destroyed or erased.  That would be the time to
  1571.  *  take the information in the TypArticle structures of each line
  1572.  *  in the document and transfer it to the line in the NetDoc document
  1573.  *  corresponding to this group.
  1574.  *
  1575.  *  This routine has to take information of the form:
  1576.  *    123:Unseen;  124:Seen; 125:Unseen; 126:Unseen; 127:Seen; 128:Seen; 129:Seen
  1577.  *  found in the TypArticle structures in consecutive lines in the document
  1578.  *  and transform it to the general form used by .newsrc files:
  1579.  *    124,127-129
  1580.  *  (though we are using our internal representation & not ASCII characters).
  1581.  *
  1582.  *    Entry    Doc      points to the document for this group.
  1583.  *
  1584.  *    Exit     The line in the Net document corresponding to this
  1585.  *              group has been updated.
  1586.  */
  1587. void
  1588. UpdateSeenArts (Doc)
  1589.      TypDoc *Doc;
  1590. {
  1591.   TypRange MyRange, *RangePtr;
  1592.   TypGroup *group;
  1593.   TypLine far * ParentLine;
  1594.   TypBlock far * ParentBlock;
  1595.   HANDLE hLine,header_handle,thread_handle;
  1596.   TypLine *LocalLinePtr;
  1597.   header_p headers;
  1598.   header_p header;
  1599.   BOOL InSeen = TRUE;
  1600.   unsigned int MyLength;
  1601.   unsigned int maxRanges;
  1602.   unsigned long artindex;
  1603.  
  1604.   /*  Get the line in the Net document that corresponds to this
  1605.    *  group.  Make a local copy of it and set RangePtr to point to
  1606.    *  the first range in that line.  We will ignore the old line's
  1607.    *  "seen" data and create the information afresh from what we
  1608.    *  have in this document.
  1609.    */
  1610.   LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
  1611.         &ParentBlock, &ParentLine);
  1612.  
  1613.   hLine = LocalAlloc (LMEM_MOVEABLE, BLOCK_SIZE);
  1614.   LocalLinePtr = (TypLine *) LocalLock (hLine);
  1615.   group = (TypGroup *) ((char *) LocalLinePtr + sizeof (TypLine));
  1616.  
  1617.   MoveBytes (ParentLine, LocalLinePtr, ParentLine->length);
  1618.  
  1619.   if (group->total_headers > 0) {
  1620.  
  1621.     header_handle = group->header_handle;
  1622.     thread_handle = group->thread_handle;
  1623.     set_index_to_identity (header_handle, thread_handle, group->total_headers);
  1624.  
  1625.     headers = lock_headers (header_handle,thread_handle);
  1626.  
  1627.     group->nRanges = 0;
  1628.  
  1629.     maxRanges = ((Doc->BlockSize - Doc->SplitSize) - ParentLine->length +
  1630.          group->nRanges * sizeof (TypRange)) / sizeof (TypRange) - 1;
  1631.  
  1632.     RangePtr = (TypRange *) ((char *) LocalLinePtr + sizeof (TypLine) +
  1633.                  RangeOffset (group->NameLen));
  1634.     MyRange.First = 1;
  1635.  
  1636.     /* Get the first line in this document.
  1637.      * If it cannot be found, just set Last=First and skip the
  1638.      * proceeding processing.  Otherwise, assume we've seen everything
  1639.      * up to but not including the first article in the document.
  1640.      */
  1641.  
  1642.     /*  LockLine (Doc->hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr); */
  1643.  
  1644.     artindex = 0;
  1645.     if (!Doc->TotalLines)
  1646.       {
  1647.     MyRange.Last = 1;
  1648.       }
  1649.     else
  1650.       {
  1651.     header = (header_elt (headers,artindex));
  1652.     MyRange.Last = header->number - 1;
  1653.  
  1654.     /* Loop to scan through the document, fabricating article ranges.
  1655.      */
  1656.     do
  1657.       {
  1658.         header = (header_elt (headers,artindex));
  1659.         if (header->Seen)
  1660.           {
  1661.         if (InSeen)
  1662.           {
  1663.             /* Continuing a sequence of seen articles.            */
  1664.             MyRange.Last = header->number;
  1665.           }
  1666.         else
  1667.           {
  1668.             /* Starting a new sequence of seen articles.          */
  1669.             MyRange.First = header->number;
  1670.             MyRange.Last = header->number;
  1671.             InSeen = TRUE;
  1672.           }
  1673.           }
  1674.         else
  1675.           {
  1676.         if (InSeen)
  1677.           {
  1678.             /* Ending a sequence of seen articles.                   */
  1679.             InSeen = FALSE;
  1680.             *(RangePtr++) = MyRange;
  1681.             (group->nRanges)++;
  1682.           }
  1683.         else
  1684.           {
  1685.             /* Continuing a sequence of unseen articles.             */
  1686.           }
  1687.           }
  1688.       }
  1689.     while (( group->nRanges < maxRanges) &&
  1690.            ((++artindex < Doc->TotalLines)));
  1691.  
  1692.     if (InSeen)
  1693.       {
  1694.         *(RangePtr++) = MyRange;
  1695.         (group->nRanges)++;
  1696.       }
  1697.       }
  1698.  
  1699.   }
  1700.  
  1701.   unlock_headers (header_handle, thread_handle);
  1702.  
  1703.   MyLength = sizeof (TypLine) + RangeOffset (group->NameLen) +
  1704.     sizeof (TypRange) * (group->nRanges) + sizeof (int);
  1705.  
  1706.   LocalLinePtr->length = MyLength;
  1707.   *(int *) ((char *) LocalLinePtr + MyLength - sizeof (int)) = MyLength;
  1708.  
  1709.   ReplaceLine (LocalLinePtr, &ParentBlock, &ParentLine);
  1710.   GlobalUnlock (ParentBlock->hCurBlock);
  1711.  
  1712.   LocalUnlock (hLine);
  1713.   LocalFree (hLine);
  1714.  
  1715. }
  1716.  
  1717. /*-- function CursorToTextLine ----------------------------------------
  1718.  *
  1719.  *   Routine to locate a text line in a document, based on the
  1720.  *   cursor position.  Used to figure out which line is being selected
  1721.  *   when a user clicks a mouse button.
  1722.  *
  1723.  *   Entry    X, Y    are the position of the cursor.
  1724.  *            DocPtr  points to the current document.
  1725.  *
  1726.  *   Exit     *LinePtr points to the current line, if one was found.
  1727.  *            *BlockPtr points to the current block, if found.
  1728.  *            Function returns TRUE iff a line was found that corresponds
  1729.  *              to the cursor position.
  1730.  */
  1731. long
  1732. NewCursorToTextLine (X, Y, DocPtr)
  1733.      int X;
  1734.      int Y;
  1735.      TypDoc *DocPtr;
  1736. {
  1737.   int SelLine;
  1738.  
  1739.   if (Y < TopSpace || (unsigned) Y > TopSpace + DocPtr->ScYLines * LineHeight ||
  1740.       X < SideSpace)
  1741.     {
  1742.       /* Cursor is in no-man's-land at edge of window.               */
  1743.     return(-1);
  1744.     }
  1745.   else
  1746.     {
  1747.       SelLine = (Y - TopSpace) / LineHeight;
  1748.     return ( DocPtr->TopLineOrd + SelLine);
  1749.     }
  1750. }
  1751.  
  1752. /* this has been changed to use header_elt (SMR) */
  1753.  
  1754. long
  1755. search_headers (TypDoc * HeaderDoc, header_p headers,
  1756.         long artindex, long num_headers)
  1757. {
  1758.  
  1759.   char_p temp;
  1760.  
  1761.   do {
  1762.     temp = (header_elt(headers,artindex))->subject;
  1763.     if (string_compare_insensitive (temp, HeaderDoc->SearchStr))
  1764.       return (artindex);        /* return the index */
  1765.   } while (artindex++ < (num_headers - 1));
  1766.  
  1767.   return (-1);                  /* not found */
  1768. }
  1769.  
  1770. char_p
  1771. string_compare_insensitive (char_p a, char far * b)
  1772. {
  1773.   int lena = lstrlen (a);
  1774.   int lenb = lstrlen (b);
  1775.   int count;
  1776.  
  1777.   for ( count = lena - lenb + 1 ; count > 0 ; count--, a++)
  1778.    if (strnicmp (a,b,lenb) == 0)
  1779.      return a;
  1780.  
  1781.   return (NULL);
  1782. }
  1783.  
  1784. long
  1785. AffectSelected (TypDoc *Doc, BOOL value, BOOL compare)
  1786. {
  1787.   TypGroup far * group;
  1788.   HANDLE header_handle,thread_handle;
  1789.   header_p headers;
  1790.   TypBlock far *BlockPtr;
  1791.   TypLine far * LinePtr;
  1792.   char_p temp;
  1793.   long index, num_affected;  
  1794.  
  1795.   LockLine (Doc->hParentBlock, Doc->ParentOffset,
  1796.             Doc->ParentLineID, &BlockPtr, &LinePtr);
  1797.   group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  1798.  
  1799.   header_handle = group->header_handle;
  1800.   thread_handle = group->thread_handle;
  1801.  
  1802.   headers = lock_headers (header_handle,thread_handle);
  1803.  
  1804.   for (index=0, num_affected=0; index < group->total_headers; index++ )
  1805.     if (compare) {
  1806.        temp = (header_elt(headers, index))->subject;
  1807.        if (string_compare_insensitive (temp, Doc->SearchStr)) {
  1808.           (header_elt(headers, index))->Selected = value;
  1809.           num_affected++;
  1810.        }
  1811.     } else {
  1812.        (header_elt(headers, index))->Selected = value;
  1813.        num_affected++;
  1814.     }           
  1815.  
  1816.   InvalidateRect(Doc->hDocWnd,NULL,FALSE);        
  1817.   return (num_affected);
  1818. }
  1819.